﻿using KinonekoSoftware.Platform.Controls;

namespace KinonekoSoftware.Platform.Core
{

    public class DialogService : Disposable, IDialogServiceAmbient, IDialogService, IBusyServiceEX, IBusyServiceAmbient
    {
        private DialogHost _host;


        public void SetHost(DialogHost host)
        {
            if (host is null)
            {
                return;
            }

            _host = host;
        }


        #region Dialog

        public Task<Result<E>> Dialog<E, TViewModel>() where TViewModel : ObjectDialog<E>
        {
            if (_host is null)
            {
                return Task.FromException<Result<E>>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            var vm = ClassStatic.CreateInstance<TViewModel>();
            return DialogImpl<E>(vm, new DialogContext());
        }

        public Task<Result<E>> Dialog<E, TViewModel>(DialogContext context) where TViewModel : ObjectDialog<E>
        {
            if (_host is null)
            {
                return Task.FromException<Result<E>>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            var vm = ClassStatic.CreateInstance<TViewModel>();

            context ??= new DialogContext();
            return DialogImpl<E>(vm, context);
        }

        public Task<Result<E>> Dialog<E>(IObjectDialog<E> viewModel)
        {
            if (_host is null)
            {
                return Task.FromException<Result<E>>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            if (viewModel is null)
            {
                return Task.FromResult(Result<E>.Failed(TextSR.Dialog_Instance_Null));
            }


            return DialogImpl<E>((ObjectDialog<E>)viewModel, new DialogContext());
        }


        public Task<Result<E>> Dialog<E>(IObjectDialog<E> viewModel, IDialogContext context)
        {
            if (_host is null)
            {
                return Task.FromException<Result<E>>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            if (viewModel is null)
            {
                return Task.FromResult(Result<E>.Failed(TextSR.Dialog_Instance_Null));
            }


            context ??= new DialogContext();
            return DialogImpl<E>((ObjectDialog<E>)viewModel, context);
        }



        public Task<Result<E>> Dialog<E>(IAsyncObjectDialog<E> viewModel)
        {
            if (_host is null)
            {
                return Task.FromException<Result<E>>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            if (viewModel is null)
            {
                return Task.FromResult(Result<E>.Failed(TextSR.Dialog_Instance_Null));
            }


            return DialogImpl<E>((AsyncObjectDialog<E>)viewModel, new DialogContext());
        }

        public Task<Result<E>> Dialog<E>(IAsyncObjectDialog<E> viewModel, IDialogContext context)
        {
            if (_host is null)
            {
                return Task.FromException<Result<E>>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            if (viewModel is null)
            {
                return Task.FromResult(Result<E>.Failed(TextSR.Dialog_Instance_Null));
            }


            context ??= new DialogContext();
            return DialogImpl<E>((AsyncObjectDialog<E>)viewModel, context);
        }
        
        public Task<Result<IList<T>>> Dialog<T>(ISelectionDialog<T> viewModel) where T : class
        {
            
            if (_host is null)
            {
                return Task.FromException<Result<IList<T>>>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            if (viewModel is null)
            {
                return Task.FromResult(Result<IList<T>>.Failed(TextSR.Dialog_Instance_Null));
            }


            return DialogImpl<IList<T>>((SelectionDialog<T>)viewModel, new DialogContext());
        }
        
        public Task<Result<IList<T>>> Dialog<T>(ISelectionDialog<T> viewModel, IDialogContext context) where T : class
        {
            if (_host is null)
            {
                return Task.FromException<Result<IList<T>>>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            if (viewModel is null)
            {
                return Task.FromResult(Result<IList<T>>.Failed(TextSR.Dialog_Instance_Null));
            }

            context ??= new DialogContext();

            return DialogImpl<IList<T>>((SelectionDialog<T>)viewModel, context);
        }


        public Task<bool> Question<E>() where E : BooleanDialog
        {
            if (_host is null)
            {
                return Task.FromException<bool>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            var vm = ClassStatic.CreateInstance<E>();
            return DialogImpl(vm, new DialogContext());
        }

        public Task<bool> Question<E>(DialogContext context) where E : BooleanDialog
        {
            if (_host is null)
            {
                return Task.FromException<bool>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            var vm = ClassStatic.CreateInstance<E>();
            return DialogImpl(vm, context ?? new DialogContext());
        }

        public Task<bool> Question(IBooleanDialog viewModel)
        {

            if (_host is null)
            {
                return Task.FromException<bool>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            if (viewModel is null)
            {
                return Task.FromException<bool>(new ArgumentNullException(TextSR.Dialog_Instance_Null));
            }


            return DialogImpl((BooleanDialog)viewModel, new DialogContext());
        }

        public Task<bool> Question(IBooleanDialog viewModel, IDialogContext context)
        {

            if (_host is null)
            {
                return Task.FromException<bool>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            if (viewModel is null)
            {
                return Task.FromException<bool>(new ArgumentNullException(TextSR.Dialog_Instance_Null));
            }


            context ??= new DialogContext();
            return DialogImpl((BooleanDialog)viewModel, context);
        }





        public Task Notify<E>() where E : NotifyDialog
        {
            if (_host is null)
            {
                return Task.FromException(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            var vm = ClassStatic.CreateInstance<E>();
            return DialogImpl(vm, new DialogContext());
        }


        public Task Notify<E>(DialogContext context) where E : NotifyDialog
        {
            if (_host is null)
            {
                return Task.FromException(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            var vm = ClassStatic.CreateInstance<E>();
            return DialogImpl(vm, context ?? new DialogContext());
        }

        public Task Notify(INotifyDialog viewModel)
        {
            if (_host is null)
            {
                return Task.FromException(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            if (viewModel is null)
            {
                return Task.FromException(new ArgumentNullException(TextSR.Dialog_Instance_Null));
            }


            return DialogImpl((NotifyDialog)viewModel, new DialogContext());
        }


        public Task<TripleSelection> Question(string description, string option1Text, string option2Text)
        {
            if (_host is null)
            {
                return Task.FromException<TripleSelection>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            var viewModel = new TripleSelectionViewModel();
            return DialogImpl(viewModel, description, option1Text, option2Text);
        }

        public Task Notify(INotifyDialog viewModel, IDialogContext context)
        {
            if (viewModel is null)
            {
                return Task.FromException(new ArgumentNullException(TextSR.Dialog_Instance_Null));
            }

            if (_host is null)
            {
                return Task.FromException(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            context ??= new DialogContext();
            return DialogImpl((NotifyDialog)viewModel, context);
        }


        private Task<Result<E>> DialogImpl<E>(AsyncObjectDialog<E> viewModel, IDialogContext context)
        {
            if (viewModel is null || context is null)
            {
                return Task.FromResult(Result<E>.Failed(TextSR.Dialog_Instance_Null));
            }

            if (_host is null)
            {
                return Task.FromException<Result<E>>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            return _host.ShowDialog<E>((DialogContext)context, viewModel);
        }

        private Task<Result<E>> DialogImpl<E>(ObjectDialog<E> viewModel, IDialogContext context)
        {
            if (viewModel is null || context is null)
            {
                return Task.FromResult(Result<E>.Failed(TextSR.Dialog_Instance_Null));
            }

            if (_host is null)
            {
                return Task.FromException<Result<E>>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }


            return _host.ShowDialog<E>((DialogContext)context, viewModel);
        }

        private Task<bool> DialogImpl(BooleanDialog viewModel, IDialogContext context)
        {
            if (viewModel is null)
            {
                return Task.FromException<bool>(new ArgumentNullException(nameof(viewModel)));
            }

            if (context is null)
            {
                return Task.FromException<bool>(new ArgumentNullException(nameof(context)));
            }

            if (_host is null)
            {
                return Task.FromException<bool>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            return _host.ShowDialog((DialogContext)context, viewModel);
        }

        private Task<TripleSelection> DialogImpl(TripleSelectionViewModel viewModel, string description, string option1Text, string option2Text)
        {
            if (viewModel is null)
            {
                return Task.FromException<TripleSelection>(new ArgumentNullException(nameof(viewModel)));
            }

            if (_host is null)
            {
                return Task.FromException<TripleSelection>(new ArgumentNullException(TextSR.Dialog_Instance_NoServiceRegistered));
            }

            return _host.ShowDialog(viewModel, description, option1Text, option2Text);
        }

        private Task DialogImpl(NotifyDialog viewModel, IDialogContext context)
        {
            if (viewModel is null)
            {
                return Task.FromException(new ArgumentNullException(nameof(viewModel)));
            }

            if (context is null)
            {
                return Task.FromException(new ArgumentNullException(nameof(context)));
            }


            if (_host is null)
            {
                return Task.FromException(new ArgumentNullException(nameof(context)));
            }

            return _host.ShowDialog((DialogContext)context, viewModel);
        }

        #endregion
        
        #region Framework

        
        public Task<bool> DangerOperation(string title, string description, int count = 0)
        {
            return Dialog(new DangerOperationViewModel
            {
                EnableCountDown = count > 0,
                CountDown       = count,
                Title           = title,
                Description     = description,
            });
        }


        public Task<bool> SuccessOperation(string title, string description, int count = 0)
        {
            return Dialog(new SuccessOperationViewModel
            {
                EnableCountDown = count > 0,
                CountDown       = count,
                Title           = title,
                Description     = description,
            });
        }

        public Task<bool> InfoOperation(string title, string description, int count = 0)
        {
            return Dialog(new InfoOperationViewModel
            {
                EnableCountDown = count > 0,
                CountDown       = count,
                Title           = title,
                Description     = description,
            });
        }

        public Task<bool> WarningOperation(string title, string description, int count = 0)
        {
            return Dialog(new WarningOperationViewModel
            {
                EnableCountDown = count > 0,
                CountDown       = count,
                Title           = title,
                Description     = description,
            });
        }

        public Task<bool> ObsoletedOperation(string title, string description, int count = 0)
        {

            return Dialog(new ObsoletedOperationViewModel
            {
                EnableCountDown = count > 0,
                CountDown       = count,
                Title           = title,
                Description     = description,
            });
        }

        public Task Danger(string title, string description, int count = 0)
        {
            return Notify(new DangerNotificationViewModel
            {
                EnableCountDown = count > 0,
                CountDown       = count,
                Title           = title,
                Description     = description,
            });
        }


        public Task Success(string title, string description, int count = 0)
        {
            return Notify(new SuccessNotificationViewModel
            {
                EnableCountDown = count > 0,
                CountDown       = count,
                Title           = title,
                Description     = description,
            });
        }
        public Task Info(string title, string description, int count = 0)
        {
            return Notify(new InfoNotificationViewModel
            {
                EnableCountDown = count > 0,
                CountDown       = count,
                Title           = title,
                Description     = description,
            });
        }
        public Task Warning(string title, string description, int count = 0)
        {
            return Notify(new WarningNotificationViewModel
            {
                EnableCountDown = count > 0,
                CountDown       = count,
                Title           = title,
                Description     = description,
            });
        }
        public Task Obsoleted(string title, string description, int count = 0)
        {
            return Notify(new ObsoletedNotificationViewModel
            {
                EnableCountDown = count > 0,
                CountDown       = count,
                Title           = title,
                Description     = description,
            });
        }

        public Task<bool> Dialog( BooleanDialog viewModel)
        {
            if (viewModel is null)
            {
                return Task.FromException<bool>(new ArgumentNullException(TextSR.Dialog_Instance_Null));
            }


            return DialogImpl(viewModel, new DialogContext());
        }
        #endregion
        
        #region IBusyService

        class BusyServiceEX(IBusyService _session, Func<string,IBusyServiceEX> callback) : IBusyServiceEX
        {
            public void Dispose()
            {
                _session.Dispose();
            }

            public Task Await(int t = 1)
            {
                return _session.Await(t);
            }

            public void Update(string text)
            {
                _session.Update(text);
            }

            public Task<IBusyServiceEX> BeginSession(string text) => Task.FromResult(callback(text));
        }

        private BusyServiceEX _session;

        private IBusyServiceEX GetSession(string text)
        {
            if (_session is null)
            {
                _session = new BusyServiceEX(_host.BeginSession(text), GetSession);
            }

            return _session;
        }
        

        public Task Await(int     t = 1) => Ioc.Get<IBusyServiceEX>().Await(t);
        public void Update(string text) => Ioc.Get<IBusyServiceEX>().Update(text);
        public Task<IBusyServiceEX> BeginSession(string text)
        {
            return Task.FromResult(GetSession(text));
        }

        #endregion
    }
}